﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RaytracerLib.Layers;
using RaytracerLib.Renderers;
using System.Drawing;
using TomanuExtensions.TestUtils;
using TomanuExtensions;
using RaytracerLib;
using RaytracerLib.Samplers;
using RaytracerLib.Resamplers;
using Raytracer.Generator;
using System.Drawing.Drawing2D;
using RaytracerLib.Resamplers.Filters;

namespace RaytracerTest
{
    [TestClass]
    public class ResamplerTilesTest : TestBase
    {
        private class ResamplerTestRenderer : BackwardRenderer
        {
            public ColorArrayFloat RenderedPixels
            {
                get
                {
                    return Canvas.RenderedPixels;
                }
            }

            public void ResampleAll()
            {
                Canvas.Resample(new Rectangle(0, 0, Film.Width, Film.Height));
            }

            public void RenderInnerTile()
            {
                RenderTile(m_tiles_yx[2, 2]);
            }

            public new void PrepareToRender()
            {
                base.PrepareToRender();
            }

            public void RenderAroundInnerTile()
            {
                RenderTile(m_tiles_yx[1, 1]);
                RenderTile(m_tiles_yx[1, 2]);
                RenderTile(m_tiles_yx[1, 3]);
                RenderTile(m_tiles_yx[2, 1]);
                RenderTile(m_tiles_yx[2, 3]);
                RenderTile(m_tiles_yx[3, 1]);
                RenderTile(m_tiles_yx[3, 2]);
                RenderTile(m_tiles_yx[3, 3]);
            }

            public ColorArrayFloat GetInnerTileAffectedRect1(out Rectangle a_r)
            {
                var rects = SplitAffectedRect(m_tiles_yx[2, 2]);
                Assert.IsTrue(rects.Count() == 1);
                a_r = rects.First();
                return Canvas.GetPostprocessedArray(a_r);
            }

            public ColorArrayFloat GetInnerTileAffectedRect2(Rectangle a_r)
            {
                return Canvas.GetPostprocessedArray(a_r);
            }
        }

        [TestMethod]
        public void Resampler_Tiles_Test()
        {
            ProgressIndicator pi = new ProgressIndicator("Resampler Tiles Test");

            int max = 2 * 4 * 4 * (3 + 7);
            int counter = 1;

            foreach (var sampler in EnumExtensions.GetValues<SamplerType>()) 
            {
                if (!(Sampler.Create(sampler) is UniformSampler))
                    continue;

                foreach (int sampler_subres in new [] { 1, 2, 3, 7 })
                {
                    foreach (var resampler in EnumExtensions.GetValues<ResamplerType>())
                    {
                        if (resampler == ResamplerType.NetResizer)
                        {
                            foreach (var filter in EnumExtensions.GetValues<InterpolationMode>())
                            {
                                if (filter == InterpolationMode.Invalid)
                                    continue;
                                if (filter == InterpolationMode.Default)
                                    continue;

                                String str = String.Format(
                                    "{0}/{1} - resampler: {2}, sampler: {3}, filter: {4}, sampler subres: {5}",
                                    counter, max, resampler, sampler, filter, sampler_subres);
                                TestContext.WriteLine(str);
                                pi.AddLine(str);
                                counter++;

                                Scene scene = PrepareScene(filter, sampler, sampler_subres);
                                TestScene(scene);
                            }
                        }
                        else
                        {
                            foreach (var filter in new[] { FilterType.Box, FilterType.Bicubic, FilterType.Lanczos })
                            {
                                String str = String.Format(
                                    "{0}/{1} - resampler: {2}, sampler: {3}, filter: {4}, sampler subres: {5}",
                                    counter, max, resampler, sampler, filter, sampler_subres);
                                TestContext.WriteLine(str);
                                pi.AddLine(str);
                                counter++;

                                Scene scene = PrepareScene(resampler, filter, sampler, sampler_subres);
                                TestScene(scene);
                            }
                        }
                    }
                }
            }
        }

        private void TestScene(Scene a_scene)
        {
            {
                Scene scene = a_scene.DeepClone();
                scene.RenderOptions.Renderer = new ResamplerTestRenderer();
                ResamplerTestRenderer renderer = (ResamplerTestRenderer)scene.CreateRenderer();
                renderer.Render();
                ColorArrayFloat ar1 = new ColorArrayFloat(renderer.RenderedPixels);
                renderer.ResampleAll();
                ColorArrayFloat ar2 = new ColorArrayFloat(renderer.RenderedPixels);
                Assert.IsTrue(ar1.EqualsContent(ar2));
            }

            {
                Scene scene = a_scene.DeepClone();
                scene.RenderOptions.Renderer = new ResamplerTestRenderer();
                ResamplerTestRenderer renderer = (ResamplerTestRenderer)scene.CreateRenderer();
                renderer.PrepareToRender();
                renderer.RenderInnerTile();
                Rectangle r;
                ColorArrayFloat ar3 = renderer.GetInnerTileAffectedRect1(out r);
                renderer.RenderAroundInnerTile();
                ColorArrayFloat ar4 = renderer.GetInnerTileAffectedRect2(r);
                renderer.ResampleAll();
                ColorArrayFloat ar5 = renderer.GetInnerTileAffectedRect2(r);

                Assert.IsTrue(ar3.EqualsContent(ar4));
                Assert.IsTrue(ar3.EqualsContent(ar5));
            }
        }

        private Scene PrepareScene(ResamplerType a_resampler_type, FilterType a_filter_type, 
            SamplerType a_sampler_type, int a_sampler_subres)
        {
            Scene scene = Scenes.Procedural_Layer();

            scene.ActiveCamera.Film.Width = 223;
            scene.ActiveCamera.Film.Height = 247;
            scene.ActiveCamera.Film.Resampler = Resampler.Create(a_resampler_type);
            (scene.ActiveCamera.Film.Resampler as FilterResampler).Filter = Filter.Create(a_filter_type);
            scene.ActiveCamera.Film.Sampler = Sampler.Create(a_sampler_type);
            (scene.ActiveCamera.Film.Sampler as UniformSampler).Subresolution = a_sampler_subres;

            return scene;
        }

        private Scene PrepareScene(InterpolationMode a_filter_type,
            SamplerType a_sampler_type, int a_sampler_subres)
        {
            Scene scene = Scenes.Procedural_Layer();

            scene.ActiveCamera.Film.Width = 223;
            scene.ActiveCamera.Film.Height = 247;
            scene.ActiveCamera.Film.Resampler = Resampler.Create(ResamplerType.NetResizer);
            (scene.ActiveCamera.Film.Resampler as NetResizerResampler).InterpolationMode = a_filter_type;
            scene.ActiveCamera.Film.Sampler = Sampler.Create(a_sampler_type);
            (scene.ActiveCamera.Film.Sampler as UniformSampler).Subresolution = a_sampler_subres;

            return scene;
        }
    }
}